
#include "stream.hpp"

#include <stdexcept>

#include <unistd.h>

using std::equal;
using std::runtime_error;
using std::string;
using std::vector;

////////////////////////////////////////////////////////////////////////////////

stream::~stream() {
}

stream::operator bool() const {
    return !ferror(get_file()) && !feof(get_file());
}

////////////////////////////////////////////////////////////////////////////////

istream& istream::operator>>(string& s) {
#define QUOTE(str) #str
#define EXPAND_AND_QUOTE(str) QUOTE(str)
#define BUF_SIZE 4096

    string crlf = "\r\n";
    char buffer[BUF_SIZE+1] = { 0 };
    char delimiter[2] = { 0 };
    int r;

    for(s = "";
        !equal(crlf.rbegin(), crlf.rend(), s.rbegin()) &&
        (r = fscanf(get_file(), "%" EXPAND_AND_QUOTE(BUF_SIZE) "[^\n]", buffer)) >= 0 &&
        (r = fscanf(get_file(), "%1[\n]", delimiter)) >= 0;
        s += buffer,
        s += delimiter,
        buffer[0] = 0,
        delimiter[0] = 0);

    if(r < 0) {
        throw stream_closed_exception();
    }

    s.erase(s.length() - crlf.length());

    return *this;

#undef EXPAND_AND_QUOTE
#undef QUOTE
#undef BUF_SIZE
}

istream& istream::operator>>(vector<char>& v) {
    if(fread(v.data(), 1, (unsigned long)v.size(), get_file()) != v.size()) {
        throw stream_closed_exception();
    }

    return *this;
}

////////////////////////////////////////////////////////////////////////////////

ostream& ostream::operator<<(ostream& (*f)(ostream&)) {
    return f(*this);
}

ostream& ostream::operator<<(const string& s) {
    fputs(s.c_str(), get_file());
    return *this;
}

ostream& ostream::operator<<(const vector<char>& v) {
    fwrite(v.data(), 1, (unsigned long)v.size(), get_file());
    return *this;
}

ostream& ostream::flush(ostream& os) {
    fflush(os.get_file());
    return os;
}

////////////////////////////////////////////////////////////////////////////////
